﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace LogoScriptIDE
{
    /// <summary>
    /// The Turtle. Controls the Canvas Window to draw
    /// </summary>
    public class Turtle
    {
        /// <summary>
        /// Turtle ctor. Construct a new Turtle for a given CanvasWindow
        /// </summary>
        /// <param name="cw">The canvas Window</param>
        public Turtle(CanvasWindow cw)
        {
            m_cw = cw;
            ResetCanvas(640,480);
            ResetStatus();
            m_updateMode = UpdateMode.UPDATE_AUTO;
        }

        /// <summary>
        /// Reset All status for the Turtle, and wipe the canvas.(Except for resetting Update Mode)
        /// </summary>
        public void ResetAll()
        {
            //Reset canvas at current size
            ResetCanvas(m_cw.canvasWidth, m_cw.canvasHeight);
            //Reset Turtle status
            ResetStatus();
        }

        /// <summary>
        /// Reset the canvas. Wiping all contents.
        /// </summary>
        /// <param name="width">The canvas width</param>
        /// <param name="height">The canvas height</param>
        public void ResetCanvas(double width, double height)
        {
            m_cw.ResetCanvas(width, height);
            AutoUpdate();
        }

        /// <summary>
        /// Resetting all status for the Turtle, except Update Mode.
        /// </summary>
        public void ResetStatus()
        {
            //Reset position , angle, pen status and background color
            Pos = new Point(m_cw.canvasWidth / 2, m_cw.canvasHeight / 2);
            Angle = -90;
            PenUp = false;
            m_penWidth = 2.0f;
            m_penColor = 0xff0000ff;
            m_backgroundColor = 0xffffffff;
            m_cw.SetCanvasBackgroundColor(m_backgroundColor);
            m_show = true;
        }

        /// <summary>
        /// Move the turtle forward,leaving behind a line if pen is down
        /// </summary>
        /// <param name="dist">Distence to move</param>
        public void MoveForward(double dist)
        {
            //Calculate the line of trace
            Point newPos = new Point(Pos.X, Pos.Y);
            newPos.X += dist * Math.Cos(Angle / 180.0 * Math.PI);
            newPos.Y += dist * Math.Sin(Angle / 180.0 * Math.PI);
            if (PenUp == false)
            {
                m_cw.DrawLine(Pos, newPos, m_penColor, m_penWidth);
                AutoUpdate();
            }
            Pos = newPos;
            if (m_show)
                AutoUpdate();
            //Not update the window only when the Turtle is hidden and the pen is up
        }

        /// <summary>
        /// Move to a position, leaving behind a line if pen is down
        /// </summary>
        /// <param name="newPos">New position</param>
        public void MoveTo(Point newPos)
        {
            if (PenUp == false)
            {
                m_cw.DrawLine(Pos, newPos, m_penColor, m_penWidth);
                AutoUpdate();
            }
            Pos = newPos;
            if (m_show)
                AutoUpdate();
        }

        /// <summary>
        /// Draw a single line of text at current location
        /// </summary>
        /// <param name="text">Text buffer</param>
        public void DrawText(string text)
        {
            m_cw.DrawText(Pos, m_penColor, text);
            AutoUpdate();
        }

        /// <summary>
        /// Rotate the Turtle clockwise
        /// </summary>
        /// <param name="angle"></param>
        public void RotateClockwise(double angle)
        {
            Angle += angle;
            Angle -= Math.Floor(Angle / 360) * 360;
            if (m_show)
                AutoUpdate();
        }

        /// <summary>
        /// Set current pen color for the Turtle.
        /// </summary>
        /// <param name="r">Red, from 0 to 255</param>
        /// <param name="g">Green, from 0 to 255</param>
        /// <param name="b">Blue, from 0 to 255</param>
        public void SetPenColor(uint r, uint g, uint b)
        {
            m_penColor = 0xff000000 + (r * 65536) + (g * 256) + b;
        }

        /// <summary>
        /// Set background color for the canvas, wiping all contents
        /// </summary>
        /// <param name="r">Red, from 0 to 255</param>
        /// <param name="g">Green, from 0 to 255</param>
        /// <param name="b">Blue, from 0 to 255</param>
        public void SetBackgroundColor(uint r, uint g, uint b)
        {
            m_backgroundColor = 0xff000000 + (r * 65536) + (g * 256) + b;
            m_cw.SetCanvasBackgroundColor(m_backgroundColor);
            AutoUpdate();
        }

        /// <summary>
        /// Set pen width
        /// </summary>
        /// <param name="newWidth">New pen width</param>
        public void SetPenWidth(double newWidth)
        {
            m_penWidth = (float)newWidth;
        }

        /// <summary>
        /// Reset the angle the Turtle is facing
        /// </summary>
        public void ResetAngle()
        {
            Angle = -90;
            if (m_show)
                AutoUpdate();
        }

        /// <summary>
        /// Set update mode
        /// </summary>
        /// <param name="mode">New update mode</param>
        public void Update(UpdateMode mode)
        {
            if (mode == UpdateMode.UPDATE_NOW)
            {
                m_cw.SetModified();
                m_cw.UpdateNow();
            }
            else
                m_updateMode = mode;
        }

        /// <summary>
        /// Update if Update Mode is Auto
        /// </summary>
        private void AutoUpdate()
        {
            if (m_updateMode == UpdateMode.UPDATE_AUTO)
                m_cw.SetModified();
        }

        /// <summary>
        /// Get/Set if the Turtle is shown on the canvas
        /// </summary>
        public bool Show
        {
            get
            {
                return m_show;
            }
            set
            {
                m_show = value;
                AutoUpdate();
            }
            
        }

        /// <summary>
        /// Getting the Dispatcher object for the canvas window
        /// </summary>
        public System.Windows.Threading.Dispatcher Dispatcher
        {
            get
            {
                return m_cw.Dispatcher;
            }
        }

        /// <summary>
        /// Update Mode
        /// </summary>
        public enum UpdateMode
        {
            /// <summary>
            /// Auto Update, All Actions will Automatically updated to the canvas window
            /// </summary>
            UPDATE_AUTO = 1,
            /// <summary>
            /// Update manually,the canvas remain unchanged if not updated mannually via UPDATE_NOW
            /// </summary>
            UPDATE_MANUAL = 2,
            /// <summary>
            /// Update manually NOW. Forcing a redraw on the canvas and block the script until a redraw is finished
            /// </summary>
            UPDATE_NOW = 3
        }

        #region Properties
        /// <summary>
        /// Current update mode
        /// </summary>
        private UpdateMode m_updateMode;
        /// <summary>
        /// If show on the canvas
        /// </summary>
        private bool m_show;
        /// <summary>
        /// If the pen is up
        /// </summary>
        public bool PenUp;
        /// <summary>
        /// Current positon
        /// </summary>
        public Point Pos;
        /// <summary>
        /// Current angle facing
        /// </summary>
        public double Angle;
        /// <summary>
        /// Currentpen cllor
        /// </summary>
        private uint m_penColor;
        /// <summary>
        /// Current pen width
        /// </summary>
        private float m_penWidth;

        /// <summary>
        /// The canvas Window drawing on
        /// </summary>
        private CanvasWindow m_cw;
        /// <summary>
        /// Current background color
        /// </summary>
        private uint m_backgroundColor;
        #endregion
    }
}
